//
// Created by cube on 2020/9/12.
//

#ifndef COMPILERDOMEA1_WINDOW_H
#define COMPILERDOMEA1_WINDOW_H

#include <functional>
#include <map>
#include <queue>

namespace NetBaseCommon {
/*
 *
 *             start_ids
 * ---------------{---------------->|       end_ids
 *                |<----------------}-------------------|
 *               start             end
 * min            |****************|                   max
 * |--------------|----------------|--------------------|
 *
 *
 *   start_ids                                       end_ids
 *  |----->|                                        |<----|
 *         end                                     start
 * min*****|                                        |******max
 *   |-----|----------------------------------------|-----|
 *
 * */

    template<typename T = uint32_t>
    class FrameIndexs {
    public:
        FrameIndexs(const T &min, const T &max, const T &default_rul_index)
                : __min_index(min), __max_index(max), __default_index(default_rul_index), __start_index(min),
                  __end_index(min), __full(false) {}

        T get() {
            T rul = __default_index;
            if (freeSize() != 0) {
                if (__end_index == __max_index) {
                    __end_index = __min_index;
                    rul = __max_index;
                } else {
                    rul = __end_index++;
                }
                if (__end_index == __start_index) {
                    __full = true;
                }
            }
            return rul;
        }

        T peek() {
            T rul = __default_index;
            if (freeSize() != 0) {
                if (__end_index == __max_index) {
                    rul = __max_index;
                } else {
                    rul = __end_index;
                }
            }
            return rul;
        }

        bool put(const T &id) {
            if (!isTally(id))return false;
            if (id == __start_index) {
                if (__start_index == __max_index)__start_index = __min_index;
                else __start_index++;
                while (!__scattered_id_starts.empty()
                       && __scattered_id_starts.top() == __start_index) {
                    __scattered_id_starts.pop();
                    if (__start_index == __max_index) {
                        __start_index = __min_index;
                        break;
                    } else {
                        __start_index++;
                    }
                }
                while (!__scattered_id_ends.empty()
                       && __scattered_id_ends.top() == __start_index) {
                    __scattered_id_ends.pop();
                    if (__start_index == __max_index) {
                        __start_index = __min_index;
                        break;
                    } else {
                        __start_index++;
                    }
                }
                if (__full)__full = false;
            } else if (id > __start_index && id <= __max_index) {
                __scattered_id_ends.push(id);
            } else if (id < __end_index && id >= __min_index) {
                __scattered_id_starts.push(id);
            }
            return true;
        }

        bool isTally(const T &id) {
            if (id < __min_index || id > __max_index)return false;
            if (__start_index < __end_index) {
                if (id < __start_index && id >= __end_index)return false;
            } else {
                if (id >= __start_index && id < __end_index)return false;
            }
            return true;
        }

        void clear() {
            while (!__scattered_id_ends.empty())__scattered_id_ends.pop();
            while (!__scattered_id_starts.empty())__scattered_id_starts.pop();
            __start_index = __end_index = __min_index;
            __full = false;
        }

        void full() {
            clear();
            __full = true;
        }

        T size() {
            if (__full) {
                return capacity();
            } else if (__start_index < __end_index) {
                return __end_index - __start_index;
            } else if (__start_index > __end_index) {
                return (__end_index - __min_index) + (__max_index - __start_index);
            }
            return 0;
        }

        T freeSize() { return capacity() - size(); }

        T capacity() { return __max_index - __min_index + 1; }

        bool empty() { return size() == 0; }

        const T &defaultIndex() { return __default_index; }

        const T &minIndex() { return __min_index; }

        const T &maxIndex() { return __max_index; }

    private:
        const T &__default_index;
        const T &__min_index;
        const T &__max_index;
        bool __full;
        T __start_index;
        T __end_index;
        std::priority_queue<T, std::deque<T>, std::greater<T>> __scattered_id_starts;
        std::priority_queue<T, std::deque<T>, std::greater<T>> __scattered_id_ends;
    };


    template<typename T, typename Id_T = uint8_t>
    class SendWindow {
    public:
        SendWindow(const Id_T &min, const Id_T &max, const Id_T &default_index,
                   const T &default_rul, const Id_T &size = 0)
                : __indexs(min, max, default_index), __default_rul(default_rul), __size(size) {}

        bool canSend() { return __window.size() < __size; }

        Id_T send(const T &frame) {
            if (!canSend() || __indexs.freeSize() == 0)
                return __indexs.defaultIndex();
            Id_T id = __indexs.get();
            __window.insert(std::pair(id, frame));
            return id;
        }

        const T &onAck(const Id_T &index) {
            if (__indexs.isTally(index)
                && __window.find(index) != __window.cend()) {
                const T &frame = __window.at(index);
                __window.erase(index);
                __indexs.put(index);
                return frame;
            }
            return __default_rul;
        }

        void reSize(const Id_T &size) { __size = size; }

        void clear() {
            __size = 0;
            __window.clear();
            __indexs.clear();
        }

    private:
        Id_T __size;
        std::map<Id_T, T> __window;
        FrameIndexs<Id_T> __indexs;
        const T& __default_rul;
    };

    template<typename T, typename Id_T = uint8_t>
    class RecvWindow {
    public:
        RecvWindow(const Id_T &min, const Id_T &max, const Id_T &default_rul_index, const T &default_recv)
                : __indexs(min, max, default_rul_index), __default_recv(default_recv) {
            __indexs.full();
        }

        bool put(const Id_T &index, const T &frame) {
            if (__indexs.put(index)) {
                __window[index] = frame;
                return true;
            }
            return false;
        }

        const T &recv() {
            if (!canRecv())return __default_recv;
            return __window.at(__indexs.get());
        }

        bool canRecv() {
            Id_T id = __indexs.peek();
            if (id == __indexs.defaultIndex())return false;
            if (__window.find(id) == __window.cend())return false;
            return true;
        }

        bool empty() { return __window.empty(); }

        const T &defaultValue() { return __default_recv; }

        void clear() {
            __window.clear();
            __indexs.full();
        }

    private:
        const T &__default_recv;
        std::map<Id_T, T> __window;
        FrameIndexs<Id_T> __indexs;
    };
}

//RecvWindow<int> window(1,10,0,-1);
//int indexs[] = {2,4,3,5,6,1,6,8};
//for (int i = 0; i < 8; i++) {
//window.put(indexs[i],indexs[i]);
//}
//
//while (window.canRecv()){
//std::cout<<int(window.recv())<<std::endl;
//}


//    int indexs[] = {2,4,3,5,6,1,7,8};
//    FrameIndexs<int>    ine(1,10,-1);
//    ine.full();
//    for (int i = 0; i < 8; i++) {
//        ine.put(indexs[i]);
//    }
//
//    while (ine.peek() != ine.defaultIndex())
//        std::cout<<ine.get()<<std::endl;
//FrameIndexs<int> indexs(1,3,0);
//std::cout<<indexs.get()<<std::endl;
//std::cout<<indexs.get()<<std::endl;
////    indexs.put(4);
//std::cout<<indexs.get()<<std::endl;
//std::cout<<indexs.get()<<std::endl;
////    indexs.put(4);
//std::cout<<indexs.get()<<std::endl;
//indexs.put(3);
//indexs.put(1);
//indexs.put(2);
////    indexs.put(3);
//std::cout<<indexs.get()<<std::endl;
//std::cout<<indexs.get()<<std::endl;
//std::cout<<indexs.get()<<std::endl;
//std::cout<<indexs.get()<<std::endl;
//std::cout<<indexs.get()<<std::endl;
//indexs.clear();
//std::cout<<indexs.get()<<std::endl;
//std::cout<<indexs.get()<<std::endl;
//std::cout<<indexs.get()<<std::endl;
//std::cout<<indexs.get()<<std::endl;

#endif //COMPILERDOMEA1_WINDOW_H
